package com.woniu.commonswitch.aop;

import com.woniu.commonswitch.annotation.ServiceSwitch;
import com.woniu.commonswitch.constant.Constant;
import com.woniu.commonswitch.exception.BusinessException;
import com.woniu.commonswitch.util.Result;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * <p>
 * 教你一招 SpringBoot + 自定义注解 + AOP 打造通用开关
 * 程序员蜗牛
 * </p>
 */
@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class ServiceSwitchAOP {

	private final StringRedisTemplate redisTemplate;

	/**
	 * 定义切点，使用了@ServiceSwitch注解的类或方法都拦截
	 */
	@Pointcut("@annotation(com.woniu.commonswitch.annotation.ServiceSwitch)")
	public void pointcut() {
	}

	@Around("pointcut()")
	public Object around(ProceedingJoinPoint point) {

		// 获取被代理的方法的参数
		Object[] args = point.getArgs();
		// 获取被代理的对象
		Object target = point.getTarget();
		// 获取通知签名
		MethodSignature signature = (MethodSignature) point.getSignature();

		try {

			// 获取被代理的方法
			Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
			// 获取方法上的注解
			ServiceSwitch annotation = method.getAnnotation(ServiceSwitch.class);

			// 核心业务逻辑
			if (annotation != null) {

				String switchKey = annotation.switchKey();
				String message = annotation.message();

				/*
				  获取配置项说明
				  这里有两种方式：1、配置加在Redis，查询时从Redis获取；
				  				  2、配置加在数据库，查询时从表获取。(MySQL单表查询其实很快，配置表其实也没多少数据)
				  我在工作中的做法：   直接放到数据字典里 同时加上缓存提高查询性能。
				 */

				// 这里我直接从redis中取，使用中大家可以按照意愿自行修改。
				String configVal = redisTemplate.opsForValue().get(switchKey);
				if (Constant.SWITCHCLOSE.equals(configVal)) {
					// 开关关闭，则返回提示。
					return new Result<>(HttpStatus.FORBIDDEN.value(), message);
				}
			}

			// 放行
			return point.proceed(args);

		} catch (Throwable e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}
}
